//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <stdlib.h>
#include "clsinfo.h"
_ELASTOS_NAMESPACE_USING
#include "sharecls.h"
#include "marshal.h"
#include <aura.h>

static CIClassInfoNode *s_pSharedClassInfoList = NULL;

static PVoid s_sharedClsInfoLock;

EXTERN void RelocateClassInfo(CIClassInfo *, CIClassInfo *);

ECode InitSharedClsInfo()
{
    //EzInitializeCriticalSection(&s_sharedClsInfoLock);
    return (ECode)AuraCriticalSection_Create(&s_sharedClsInfoLock);
}

void DeleteSharedClsInfo()
{
    //EzDeleteCriticalSection(&s_sharedClsInfoLock);
    AuraCriticalSection_Destroy(s_sharedClsInfoLock);
}

ECode RegisterSharedClassInfo(
    /* [in] */ CIClassInfo *pClassInfo)
{
    CIClassInfo * pShareClassInfo;
    CIClassInfoNode *pNewNode;
    CIClassInfoNode *pCurNode;
    CIClassEntry    *pClassDir;
    CIInterfaceEntry *pInterfaceDir;

    assert (NULL != pClassInfo);

    // TODO: compare component uuid, need car support
    // prevent from registering same ClassInfo again
    //
    AuraCriticalSection_Enter(s_sharedClsInfoLock);
    if (pClassInfo->classNum != 0) {
        pClassDir = (CIClassEntry *)((UInt32)pClassInfo->classDir \
                            + (UInt32)pClassInfo);
        pCurNode = s_pSharedClassInfoList;
        while (pCurNode != NULL) {
            if (pCurNode->m_pClsInfo->classNum != 0 \
                && pClassDir[0].clsid == \
                pCurNode->m_pClsInfo->classDir[0].clsid) {
                break;
            }
            pCurNode = pCurNode->m_pNext;
        }
    }
    else if (pClassInfo->interfaceNum != 0) {
        pInterfaceDir = (CIInterfaceEntry *)((UInt32)pClassInfo->interfaceDir \
                                + (UInt32)pClassInfo);
        pCurNode = s_pSharedClassInfoList;
        while (pCurNode != NULL) {
            if (pCurNode->m_pClsInfo->interfaceNum != 0 \
                && pInterfaceDir[0].iid == \
                pCurNode->m_pClsInfo->interfaceDir[0].iid) {
                break;
            }
            pCurNode = pCurNode->m_pNext;
        }
    }
    else {
        AuraCriticalSection_Leave(s_sharedClsInfoLock);
        // No class info need to register
        return NOERROR;
    }

    if (pCurNode != NULL) {
        AuraCriticalSection_Leave(s_sharedClsInfoLock);
        return S_ALREADY_EXISTS;
    }

    pShareClassInfo = (CIClassInfo *)Aura_malloc((Aura_Int32)pClassInfo->totalSize);
    AuraCriticalSection_Leave(s_sharedClsInfoLock);
    if (NULL == pShareClassInfo) {
        return E_OUT_OF_MEMORY;
    }
    RelocateClassInfo(pClassInfo, pShareClassInfo);

    pNewNode = (CIClassInfoNode *)Aura_malloc(sizeof(CIClassInfoNode));
    if (pNewNode == NULL) {
        return E_OUT_OF_MEMORY;
    }
    pNewNode->m_pClsInfo = (CIClassInfo *)pShareClassInfo;
    pNewNode->m_pNext = NULL;
    AuraCriticalSection_Enter(s_sharedClsInfoLock);
    if (s_pSharedClassInfoList == NULL) {
        s_pSharedClassInfoList = pNewNode;
    }
    else {
        pNewNode->m_pNext = s_pSharedClassInfoList->m_pNext;
        s_pSharedClassInfoList->m_pNext = pNewNode;
    }
    AuraCriticalSection_Leave(s_sharedClsInfoLock);

    return NOERROR;
}

ECode UnregisterSharedClassInfo(
    /* [in] */ CIClassInfo *pClassInfo)
{
    CIClassInfoNode *pCurNode, *pPreNode;

    assert(pClassInfo != NULL);

    AuraCriticalSection_Enter(s_sharedClsInfoLock);
    pPreNode = NULL;
    pCurNode = s_pSharedClassInfoList;
    while (pCurNode != NULL) {
        if ((*pCurNode).m_pClsInfo == pClassInfo) {
            break;
        }
        pPreNode = pCurNode;
        pCurNode = pCurNode->m_pNext;
    }

    if (pCurNode != NULL) {
        if (pPreNode == NULL) {
            s_pSharedClassInfoList = pCurNode->m_pNext;
        }
        else {
            pPreNode->m_pNext = pCurNode->m_pNext;
        }
        AuraCriticalSection_Leave(s_sharedClsInfoLock);
        Aura_free(pCurNode);

        return NOERROR;
    }

    AuraCriticalSection_Leave(s_sharedClsInfoLock);
    return E_DOES_NOT_EXIST;
}

ECode LookupSharedClassEntry(
    REMuid rclsid,
    CIClassEntry **ppClassEntry)
{
    CIClassInfoNode *pCurNode;
    CIClassInfo *pClsInfo;
    int m;

    assert(ppClassEntry != NULL);

    AuraCriticalSection_Enter(s_sharedClsInfoLock);
    pCurNode = s_pSharedClassInfoList;
    while (pCurNode != NULL) {
        pClsInfo = pCurNode->m_pClsInfo;
        for (m = 0; m < pClsInfo->classNum; m++) {
            if (pClsInfo->classDir[m].clsid == rclsid) {
                *ppClassEntry = &(pClsInfo->classDir[m]);
                AuraCriticalSection_Leave(s_sharedClsInfoLock);
                return NOERROR;
            }
        }
        pCurNode = pCurNode->m_pNext;
    }
    AuraCriticalSection_Leave(s_sharedClsInfoLock);
//    MARSHAL_DBGOUT(MSHDBG_NORMAL, DUMP_GUID(rclsid));

    return E_DOES_NOT_EXIST;
}

ECode LookupSharedClassInfo(
    REMuid rclsid,
    CIClassInfo **ppClassInfo)
{
    CIClassInfoNode *pCurNode;
    CIClassInfo *pClsInfo;
    int m;

    assert(ppClassInfo != NULL);

    AuraCriticalSection_Enter(s_sharedClsInfoLock);
    pCurNode = s_pSharedClassInfoList;
    while (pCurNode != NULL) {
        pClsInfo = pCurNode->m_pClsInfo;
        for (m = 0; m < pClsInfo->classNum; m++) {
            if (pClsInfo->classDir[m].clsid == rclsid) {
                *ppClassInfo = pClsInfo;
                AuraCriticalSection_Leave(s_sharedClsInfoLock);
                return NOERROR;
            }
        }
        pCurNode = pCurNode->m_pNext;
    }
    AuraCriticalSection_Leave(s_sharedClsInfoLock);

    return E_DOES_NOT_EXIST;
}

INLINE void  UnrelocateUnalignedPtr(void *pPtr, size_t offset)
{
#ifdef _x86
    *(size_t *)pPtr -= offset;
#elif defined(_arm)
    union
    {
        size_t  s;
        byte_t  bytes[4];
    } u;
    u.bytes[0] = ((byte_t *)pPtr)[0];
    u.bytes[1] = ((byte_t *)pPtr)[1];
    u.bytes[2] = ((byte_t *)pPtr)[2];
    u.bytes[3] = ((byte_t *)pPtr)[3];

    u.s -= offset;

    ((byte_t *)pPtr)[0] = u.bytes[0];
    ((byte_t *)pPtr)[1] = u.bytes[1];
    ((byte_t *)pPtr)[2] = u.bytes[2];
    ((byte_t *)pPtr)[3] = u.bytes[3];
#else
#error unsupported architecure.
#endif
}

ECode UnrelocateClassInfo(CIClassInfo *pInClsInfoData,
    CIClassInfo * pDestClsInfo)
{
    Int32 i, j;
    CIClassEntry * pClassDir;
    CIInterfaceEntry ** ppInterfaceDir;
    CIInterfaceEntry * pInterfaceDir;
    CIMethodEntry * pMethodDir;

    memcpy(pDestClsInfo, pInClsInfoData, *(UInt32*)pInClsInfoData);
    pDestClsInfo->interfaceDir = (CIInterfaceEntry*) \
            ((Byte*)pDestClsInfo->interfaceDir - (UInt32)pInClsInfoData);
    pInterfaceDir = (CIInterfaceEntry*)((Byte*) pDestClsInfo->interfaceDir + \
                      (UInt32)pDestClsInfo);

    for (i = 0; i < pDestClsInfo->interfaceNum; i++) {
        pInterfaceDir[i].methods = (CIMethodEntry *) \
              ((Byte*)pInterfaceDir[i].methods - (UInt32)pInClsInfoData);

        pMethodDir = (CIMethodEntry*)((Byte*)pInterfaceDir[i].methods + \
                            (UInt32)pDestClsInfo);
        for (j = 0; j < pInterfaceDir[i].methodNumMinus3; j++) {
            pMethodDir[j].params = (CIBaseType*) \
                 ((Byte*)pMethodDir[j].params - (UInt32)pInClsInfoData);
        }
    }

    pDestClsInfo->classDir = (CIClassEntry*) \
          ((Byte*)pDestClsInfo->classDir - (UInt32)pInClsInfoData);

    pClassDir = (CIClassEntry*)((Byte*)pDestClsInfo->classDir + (UInt32)pDestClsInfo);
    for (i = 0; i < pDestClsInfo->classNum; i++) {
        pClassDir[i].interfaces = (CIInterfaceEntry**) \
             ((Byte*)pClassDir[i].interfaces - (UInt32)pInClsInfoData);

        ppInterfaceDir = (CIInterfaceEntry**)((Byte*)pClassDir[i].interfaces + (UInt32)pDestClsInfo);
        for (j = 0; j < pClassDir[i].interfaceNum; j++) {
            UnrelocateUnalignedPtr(ppInterfaceDir + j, (size_t)pInClsInfoData);
        }
        pClassDir[i].pszUunm =  \
            (char*)((Byte*)pClassDir[i].pszUunm - (UInt32)pInClsInfoData);
    }

    return NOERROR;
}
